package system

import (
	consts2 "MoSkeleton/config/consts"
	"MoSkeleton/framework"
	"MoSkeleton/framework/core/dto/response"
	"MoSkeleton/framework/core/models"
	"MoSkeleton/framework/utils"
	"MoSkeleton/models/base"
	"MoSkeleton/models/system"
	s1 "MoSkeleton/web/pogos/request/system"
	system2 "MoSkeleton/web/pogos/respose/system"
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"github.com/jinzhu/copier"
	"gorm.io/gorm"
	"strconv"
)

type SysConfigService struct {
}

func (s *SysConfigService) GetConfigPage(configReq *s1.SysConfigPageReq) (*response.PageResult, error) {
	db := s.getConfigListVo(configReq)
	result := make([]*system2.SysConfigItemResp, 0)
	var total int64
	if err := db.Count(&total).Offset(configReq.Offset()).Limit(configReq.Limit()).Find(&result).Error; err != nil {
		return nil, err
	}
	pageResult := response.NewFromPageInfo(&configReq.PageInfo)
	pageResult.Rows = &result
	pageResult.Total = total
	return pageResult, nil
}

func (s *SysConfigService) ExportConfig(configReq *s1.SysConfigPageReq) (*bytes.Buffer, error) {
	db := s.getConfigListVo(configReq)
	itemsResp := make([]*system2.SysConfigItemResp, 0)
	if err := db.Find(&itemsResp).Error; err != nil {
		return nil, err
	}
	titles := []string{"参数编号", "参数名称", "参数键名", "参数键值", "系统内置"}
	exportFile := utils.NewExportExcelFile()

	return exportFile.Export(consts2.Excel_Sheet_Config, &titles, func(row, col int, colTitle string) (value any, ok bool, err error) {
		if len(itemsResp) <= row {
			return "", false, nil
		}
		itemResp := itemsResp[row]
		switch col {
		case 0:
			return "'" + strconv.FormatUint(itemResp.ID, 10), true, nil
		case 1:
			return itemResp.ConfigName, true, nil
		case 2:
			return itemResp.ConfigKey, true, nil
		case 3:
			return itemResp.ConfigValue, true, nil
		case 4:
			if itemResp.ConfigType == consts2.U_YES {
				return "是", true, nil
			} else {
				return "否", true, nil
			}
		}
		return "", false, nil
	})
}
func (s *SysConfigService) AddConfig(addReq *s1.SysConfigAddReq) (bool, error) {
	sysConfig := &system.SysConfig{}
	if err := copier.Copy(sysConfig, addReq); err != nil {
		return false, err
	}
	result := base.BaseDb.Save(sysConfig)
	if result.Error == nil && result.RowsAffected > 0 {
		_ = s.loadToCacheById(sysConfig.ID)
	}
	return result.RowsAffected > 0, result.Error
}

func (s *SysConfigService) DeleteById(id uint64) (bool, error) {
	result := base.BaseDb.Delete(&system.SysConfig{MoModel: models.MoModel{ID: id}})
	framework.MoRedis.Del(context.Background(), consts2.RedisKey_Sys_Config+strconv.FormatUint(id, 10))
	return result.RowsAffected > 0, result.Error
}

func (s *SysConfigService) RefreshCache() error {
	if err := s.clearConfigCache(); err != nil {
		return err
	}
	if err := s.loadConfigCache(); err != nil {
		return err
	}
	return nil
}

func (s *SysConfigService) UpdateConfigById(updateReq *s1.SysConfigUpdateReq) (bool, error) {
	result := base.BaseDb.Model(&system.SysConfig{}).Where("id = ?", updateReq.ID).Updates(map[string]interface{}{
		"config_key":   updateReq.ConfigKey,
		"config_name":  updateReq.ConfigName,
		"config_type":  updateReq.ConfigType,
		"config_value": updateReq.ConfigValue,
		"remark":       updateReq.Remark,
		"updated_by":   updateReq.UpdatedBy,
	})
	if result.Error == nil {
		if err := s.loadToCacheById(updateReq.ID); err != nil {
			return false, err
		}
	}
	return result.RowsAffected > 0, result.Error
}

func (s *SysConfigService) GetConfigById(id uint64) (*system2.SysConfigItemResp, error) {
	itemResp, err := s.getFromCacheById(id)
	if err != nil || itemResp != nil {
		return itemResp, err
	}
	itemResp = &system2.SysConfigItemResp{}
	if err := base.BaseDb.Model(&system.SysConfig{}).Where("id = ?", id).First(itemResp).Error; err != nil {
		return nil, err
	}
	_ = s.setCacheById(id, itemResp)
	return itemResp, nil
}

func (s *SysConfigService) GetByConfigKey(configKey string) (*system2.SysConfigItemResp, error) {
	itemResp := &system2.SysConfigItemResp{}
	if err := base.BaseDb.Model(&system.SysConfig{}).Where("config_key = ?", configKey).First(itemResp).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return itemResp, nil
}
func (s *SysConfigService) loadToCacheById(id uint64) error {
	itemResp := &system2.SysConfigItemResp{}
	if err := base.BaseDb.Model(&system.SysConfig{}).Where("id = ?", id).First(itemResp).Error; err != nil {
		return err
	}
	return s.setCacheById(id, itemResp)
}

func (s *SysConfigService) setCacheById(id uint64, itemResp *system2.SysConfigItemResp) error {
	buffers, err := json.Marshal(itemResp)
	if err != nil {
		return err
	}
	statusCmd := framework.MoRedis.Set(context.Background(), consts2.RedisKey_Sys_Config+strconv.FormatUint(id, 10), buffers, utils.RedisExpireDuration(consts2.Redis_Expire_Time))
	return statusCmd.Err()
}
func (s *SysConfigService) getFromCacheById(id uint64) (*system2.SysConfigItemResp, error) {
	dataBytes, err := framework.MoRedis.Get(context.Background(), consts2.RedisKey_Sys_Config+strconv.FormatUint(id, 10)).Bytes()
	if err != nil || dataBytes == nil {
		return nil, err
	}
	itemResp := &system2.SysConfigItemResp{}
	if err := json.Unmarshal(dataBytes, itemResp); err != nil {
		return nil, err
	}
	return itemResp, nil
}
func (s *SysConfigService) clearConfigCache() error {
	strSliceCmd := framework.MoRedis.Keys(context.Background(), consts2.RedisKey_Sys_Config+"*")
	if strSliceCmd.Err() != nil {
		return strSliceCmd.Err()
	}
	return framework.MoRedis.Del(context.Background(), strSliceCmd.Val()...).Err()
}

func (s *SysConfigService) loadConfigCache() error {
	configReq := &s1.SysConfigPageReq{}
	db := s.getConfigListVo(configReq)
	result := make([]*system2.SysConfigItemResp, 0)
	if err := db.Find(&result).Error; err != nil {
		return err
	}
	for _, itemResp := range result {
		dataBytes, err := json.Marshal(itemResp)
		if err != nil {
			return err
		}
		framework.MoRedis.Set(context.Background(), consts2.RedisKey_Sys_Config+strconv.FormatUint(itemResp.ID, 10), dataBytes, utils.RedisExpireDuration(consts2.Redis_Expire_Time))
	}
	return nil
}

/*
select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark

		from sys_config
	 <where>
				<if test="configName != null and configName != ''">
					AND config_name like concat('%', #{configName}, '%')
				</if>
				<if test="configType != null and configType != ''">
					AND config_type = #{configType}
				</if>
				<if test="configKey != null and configKey != ''">
					AND config_key like concat('%', #{configKey}, '%')
				</if>
				<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
					and date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
				</if>
				<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
					and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
				</if>
			</where>
*/
func (s *SysConfigService) getConfigListVo(configReq *s1.SysConfigPageReq) *gorm.DB {
	db := base.BaseDb.Model(&system.SysConfig{})
	if configReq.ConfigName != "" {
		db.Where("config_name like ?", "%"+configReq.ConfigName+"%")
	}
	if configReq.ConfigType != "" {
		db.Where("config_type = ?", configReq.ConfigType)
	}
	if configReq.ConfigKey != "" {
		db.Where("config_key like ?", "%"+configReq.ConfigKey+"%")
	}
	beginTime, ok := configReq.GetParamValue("beginTime")
	if ok && beginTime != "" {
		if beginTime = configReq.FormatBeginTime(beginTime); beginTime != "" {
			db.Where("created_at >= ?", beginTime)
		}
	}
	endTime, ok := configReq.GetParamValue("endTime")
	if ok && endTime != "" {
		if endTime = configReq.FormatEndTime(endTime); endTime != "" {
			db.Where("created_at <= ?", endTime)
		}
	}
	return db
}
